/**************************************************************************
*   Copyright (C) 2009 Chernov Dmitry                                     *
*   diman4ik.chernov@gmail.com                                            *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/

#ifndef KORNERS_GAME_ENGINE
#define KORNERS_GAME_ENGINE

#include <QVector>
#include <QObject>
#include <QPoint>
#include <KDebug>


class KornersMove
{
public:
    QVector<QPoint> points;
};

/**
 *  Base engine class
 */
class KornersEngine : public QObject
{
    Q_OBJECT
public:
    explicit KornersEngine( QObject* parent = 0 );
    virtual ~KornersEngine(){}
    static const int FIELD_SIZE = 8;
    static const int N_CAPS = 24;
    static const int IN_ROW = 4;
    static const int N_ROWS = 3;

    /**
     *  Making the move
     *  This is implemented in engines
     */
    virtual void makeNextMove( const QPoint& src, const QPoint& dst ) = 0;
    virtual void stop();

    virtual bool canSelect( const QPoint& src ) const = 0;
    virtual bool canMove( QPoint src, QPoint dst ) const = 0;

    enum direction
    {
        none = 0,
        north = 1,
        east = 2,
        south = 3,
        west = 4,
    };

    enum owner
    {
        empty = 0,
        player = 1,
        opponent = 2,
    };

public slots:
    /**
     *  Player made his move
     */
    void playersMove( QPoint src, QPoint dst );
    /**
     *  Player selected cap
     */
    void capSelected( QPoint cap );

signals:
    /**
     *  Engines's move
     */
    void move( KornersMove mv );
    /**
     *  all routes player can take from point
     */
    void availableMoves( const QVector<QPoint>* moves );

    void engineMessage( QString txt );
    void canPlay();
    void youWin();
    void youLost();

protected:  
    /**
     *  Find all routes player can take from point
     */
    void getAvailableMoves( QPoint from_where );
    /**
     *  Here we get result from engine
     */
    void computerMove( QPoint src, QPoint dst );
    /**
     *  Finding routes available from point
     */
    bool tryJump( QPoint& src, direction dir, bool* saveThere ) const;
    void markReachable( QPoint src, direction dir,  bool* saveThere, bool jump = false ) const;

    char m_field[ FIELD_SIZE*FIELD_SIZE ];

    inline void applyMove( const QPoint& src, const QPoint& dst )
    {
	m_field[ pointToIndex(dst) ] = m_field[ pointToIndex(src) ];
	m_field[ pointToIndex(src) ] = empty;
    }

    inline void applyMove( const int src, const int dst )
    {
        m_field[dst] = m_field[src];
        m_field[src] = empty;
    }

    /**
     *  Helping routine for debugging
     */
    inline void printField() const
    {
        for(int i = 0; i < FIELD_SIZE; i++)
        {
            kDebug() << int(m_field[i*FIELD_SIZE + 0]) << int(m_field[i*FIELD_SIZE + 1]) 
                     << int(m_field[i*FIELD_SIZE + 2]) << int(m_field[i*FIELD_SIZE + 3]) 
                     << int(m_field[i*FIELD_SIZE + 4]) << int(m_field[i*FIELD_SIZE + 5])
                     << int(m_field[i*FIELD_SIZE + 6]) << int(m_field[i*FIELD_SIZE + 7]);
        }
    }

    inline int pointToIndex( const QPoint& pnt ) const
    {
	return pnt.x() + pnt.y()*FIELD_SIZE;
    }

    inline QPoint indexToPoint( int index ) const
    {
        return QPoint( index%FIELD_SIZE, index/FIELD_SIZE );
    }

    inline int rotateIndex( int i ) const
    {
	return FIELD_SIZE - i%FIELD_SIZE  - 1 + (FIELD_SIZE - i/FIELD_SIZE - 1)*FIELD_SIZE;
    }

    inline QPoint rotatePoint( const QPoint& p ) const
    {
        return QPoint( FIELD_SIZE - p.x() - 1, FIELD_SIZE - p.y() - 1 );
    }

    /**
     *  Find out if game over and who won
     */
    inline owner gameOver() const
    {
        for(int i = 0; i < N_CAPS; i++)
        {
            if(i < N_CAPS/2)
            {
                if( m_field[i%4 + (i/4)*FIELD_SIZE] != player )
                {
                    i = N_CAPS/2 - 1;
                    continue;
                }
                if( i == N_CAPS/2 - 1)
                    return player;
            }
            else
            {
                if( m_field[i%IN_ROW + IN_ROW + ( (i - IN_ROW*N_ROWS)/IN_ROW + FIELD_SIZE - N_ROWS )*FIELD_SIZE] != opponent )
                    return empty;
            }
        }

        return opponent;
    }   

    inline bool isMoveValid( QPoint src, QPoint dst, owner who ) const
    {
        if( ( m_field[ pointToIndex(dst) ] == empty ) && 
            ( m_field[ pointToIndex(src) ] == who ) )
            return true;
        return false;
    }

private:
    /**
     *  Constructs route
     */
    bool makePath(QPoint src, QPoint dst, KornersMove& mv);
};

#endif
